/**   
 * Copyright 2011 COMERE Harold
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package etrl.core;

import etrl.shaders.DynamicShaderProgram;
import etrl.shaders.GLSLType;
import etrl.shaders.ShaderStage;
import etrl.shaders.Variable;
import etrl.shaders.VariableForwardMode;
import etrl.shaders.VariableModifier;

/**
 * \class DefaultShader 
 * \brief Default shader used if no one is defined. 
 * It is limited to 2 textures and 3 lights applied on the same object.
 * @author harold
 *
 */
public class DefaultShader extends DynamicShaderProgram
{	
	/**
	 * \brief Defines and build the shader.
	 */
	public DefaultShader()
	{
		addUniformVariable("lightData", new GLSLType(GLSLType.Type.VEC4), VariableForwardMode.FRAGMENT_ONLY, MAXIMUM_NUMBER_OF_LIGHTS, DynamicShaderProgram.Binding.BINDING_LIGHT_DATA);
		addUniformVariable("lightColor", new GLSLType(GLSLType.Type.VEC3), VariableForwardMode.FRAGMENT_ONLY, MAXIMUM_NUMBER_OF_LIGHTS, DynamicShaderProgram.Binding.BINDING_LIGHT_COLOR);
		addUniformVariable("lightCount", new GLSLType(GLSLType.Type.INT), VariableForwardMode.FRAGMENT_ONLY, 1, DynamicShaderProgram.Binding.BINDING_LIGHT_COUNT);
	
		addUniformVariable("tex0", new GLSLType(GLSLType.Type.SAMPLER2D), VariableForwardMode.FRAGMENT_ONLY, 1, DynamicShaderProgram.Binding.BINDING_TEXTURE0);
		addUniformVariable("tex1", new GLSLType(GLSLType.Type.SAMPLER2D), VariableForwardMode.FRAGMENT_ONLY, 1, DynamicShaderProgram.Binding.BINDING_TEXTURE1);

		addUniformVariable("textureCount", new GLSLType(GLSLType.Type.INT), VariableForwardMode.VERTEX_AND_FRAGMENT, 1, DynamicShaderProgram.Binding.BINDING_TEXTURE_COUNT);
		
		addUniformVariable("modelViewMatrix", new GLSLType(GLSLType.Type.MAT4), VariableForwardMode.VERTEX_ONLY, 1, DynamicShaderProgram.Binding.BINDING_MODELVIEW_MATRIX);
		addUniformVariable("projectionMatrix", new GLSLType(GLSLType.Type.MAT4), VariableForwardMode.VERTEX_ONLY, 1, DynamicShaderProgram.Binding.BINDING_PROJECTION_MATRIX);
		addUniformVariable("normalMatrix", new GLSLType(GLSLType.Type.MAT3), VariableForwardMode.VERTEX_ONLY, 1, DynamicShaderProgram.Binding.BINDING_NORMAL_MATRIX);
		
		addUniformVariable("textureMatrix0", new GLSLType(GLSLType.Type.MAT4), VariableForwardMode.VERTEX_ONLY, 1, DynamicShaderProgram.Binding.BINDING_TEXTURE_MATRIX0);
		addUniformVariable("textureMatrix1", new GLSLType(GLSLType.Type.MAT4), VariableForwardMode.VERTEX_ONLY, 1, DynamicShaderProgram.Binding.BINDING_TEXTURE_MATRIX1);

		addCustomVariable("texCoord", new GLSLType(GLSLType.Type.VEC4), VariableForwardMode.VERTEX_AND_FRAGMENT, 1);
	
		addCustomVariable("worldPos", new GLSLType(GLSLType.Type.VEC4), VariableForwardMode.VERTEX_AND_FRAGMENT, 1);
	
		VariableModifier modelViewMatrixmodifier = getNewVariableModifier("ModelViewMatrixModifier", "va_Position = modelViewMatrix * va_Position;", ShaderStage.VERTEX);
		if (modelViewMatrixmodifier != null)
		{
			Variable positionVariable = getVariable("va_Position");
	
			if (positionVariable != null)
				modelViewMatrixmodifier.addVariable(positionVariable, VariableModifier.VariableQualifier.INOUT);
		}
	
		VariableModifier worldPosModifier = getNewVariableModifier("WorldPosModifier", "worldPos = va_Position;", ShaderStage.VERTEX);
		if (worldPosModifier != null)
		{
			Variable worldPosVariable = getVariable("worldPos");
			Variable positionVariable = getVariable("va_Position");
	
			if (worldPosVariable != null && positionVariable != null)
			{
				worldPosModifier.addVariable(worldPosVariable, VariableModifier.VariableQualifier.INOUT);
				worldPosModifier.addVariable(positionVariable, VariableModifier.VariableQualifier.IN);
			}
		}
	
		VariableModifier projectionMatrixmodifier = getNewVariableModifier("ProjectionMatrixModifier", "va_Position = projectionMatrix * va_Position;", ShaderStage.VERTEX);
		if (projectionMatrixmodifier != null)
		{
			Variable positionVariable = getVariable("va_Position");
	
			if (positionVariable != null)
				projectionMatrixmodifier.addVariable(positionVariable, VariableModifier.VariableQualifier.INOUT);
		}
	
		VariableModifier normalMatrixModifier = getNewVariableModifier("NormalMatrixModifier", "va_Normal = normalMatrix * va_Normal;", ShaderStage.VERTEX);
		if (normalMatrixModifier != null)
		{
			Variable normalVariable = getVariable("va_Normal");
	
			if (normalVariable != null)
				normalMatrixModifier.addVariable(normalVariable, VariableModifier.VariableQualifier.INOUT);
		}
		
		VariableModifier texCoordModifier = getNewVariableModifier("TexCoordModifier",
				"texCoord = vec4(0., 0., 0., 0.);" +
				"if(textureCount > 0) texCoord.xy = (textureMatrix0 * vec4(va_TexCoord.x, va_TexCoord.y, 0., 1.)).xy;" +
				"if(textureCount > 1) texCoord.zw = (textureMatrix1 * vec4(va_TexCoord.x, va_TexCoord.y, 0., 1.)).xy;"
				, ShaderStage.VERTEX);
		if(texCoordModifier != null)
		{
			Variable texCoordVariable = getVariable("va_TexCoord");
			Variable outTexCoordVariable = getVariable("texCoord");
			
			if(texCoordVariable != null && outTexCoordVariable != null)	
			{
				texCoordModifier.addVariable(texCoordVariable, VariableModifier.VariableQualifier.IN);			
				texCoordModifier.addVariable(outTexCoordVariable, VariableModifier.VariableQualifier.INOUT);
			}
		}
	
		VariableModifier colorInitializer = getNewVariableModifier("ColorInitializer", "biu_CustomColor = vec4(1.0, 1.0, 1.0, 1.0);", ShaderStage.FRAGMENT);
		if (colorInitializer != null)
		{
			Variable customColorVariable = getVariable("biu_CustomColor");
	
			if (customColorVariable != null)
			{
				colorInitializer.addVariable(customColorVariable, VariableModifier.VariableQualifier.INOUT);
			}
		}
	
		VariableModifier colorModifier = getNewVariableModifier("ColorModifier", "biu_CustomColor *= va_Color;", ShaderStage.FRAGMENT);
		if (colorModifier != null)
		{
			Variable colorVariable = getVariable("va_Color");
			Variable customColorVariable = getVariable("biu_CustomColor");
	
			if (colorVariable != null && customColorVariable != null)
			{
				colorModifier.addVariable(customColorVariable, VariableModifier.VariableQualifier.INOUT);
				colorModifier.addVariable(colorVariable, VariableModifier.VariableQualifier.IN);
			}
		}
	
		VariableModifier phongModifier = getNewVariableModifier("PhongModifier", 
				"vec4 tot_diffuse = vec4(0., 0., 0., 1.);" +
				"if(lightCount > 0)" +
				"{" +
					"vec3 L = lightData[0].xyz - worldPos.xyz;\n" + 
					"L = normalize(L);\n" +
					"vec3 thenormal = normalize(va_Normal);\n" +
					"float dot = max(0., dot(L,thenormal));\n" +
					"tot_diffuse += vec4(lightColor[0], 1.) * dot;" +
				"}" +
				"if(lightCount > 1)" +
				"{" +
					"vec3 L = lightData[1].xyz - worldPos.xyz;\n" + 
					"L = normalize(L);\n" +
					"vec3 thenormal = normalize(va_Normal);\n" +
					"float dot = max(0., dot(L,thenormal));\n" +
					"tot_diffuse += vec4(lightColor[1], 1.) * dot;" +
				"}"	+
				"if(lightCount > 2)" +
				"{" +
					"vec3 L = lightData[2].xyz - worldPos.xyz;\n" + 
					"L = normalize(L);\n" +
					"vec3 thenormal = normalize(va_Normal);\n" +
					"float dot = max(0., dot(L,thenormal));\n" +
					"tot_diffuse += vec4(lightColor[2], 1.) * dot;" +
				"}"	+
				"if(lightCount > 0)" +
				"{" +
					"biu_CustomColor *= tot_diffuse;" +
					"biu_CustomColor += vec4(0.2, 0.2, 0.2, 0.);" +
				"}"
				,ShaderStage.FRAGMENT);
	
		if (phongModifier != null)
		{
			Variable customColorVariable = getVariable("biu_CustomColor");
			Variable worldPosVariable = getVariable("worldPos");
			Variable normalVariable = getVariable("va_Normal");
	
			if (customColorVariable != null && worldPosVariable != null && normalVariable != null)
			{
				phongModifier.addVariable(customColorVariable, VariableModifier.VariableQualifier.INOUT);
				phongModifier.addVariable(worldPosVariable, VariableModifier.VariableQualifier.IN);
				phongModifier.addVariable(normalVariable, VariableModifier.VariableQualifier.IN);
			}
		}
	
		VariableModifier textureModifier = getNewVariableModifier("TextureModifier", 
		"if(textureCount > 0) biu_CustomColor *= texture2D(tex0, texCoord.xy);" +
		"if(textureCount > 1) biu_CustomColor *= texture2D(tex1, texCoord.zw);"
			, ShaderStage.FRAGMENT);
		if (textureModifier != null)
		{
			Variable customColorVariable = getVariable("biu_CustomColor");
			Variable texCoordVariable = getVariable("texCoord");
	
			if (customColorVariable != null && texCoordVariable != null)
			{
				textureModifier.addVariable(customColorVariable, VariableModifier.VariableQualifier.INOUT);
				textureModifier.addVariable(texCoordVariable, VariableModifier.VariableQualifier.IN);
			}
		}
	
		buildProgram();
	
		if (!valid())
			System.out.println("Shader program not valid");
	}
}
